﻿using System;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Automation.Text;

namespace QuickUIT
{
    /// <summary>
    /// Provides functionality for invoking control patterns on AutomationElements.
    /// </summary>
    public static class AutomationElementPatterns
    {
        /// <summary>
        /// Gets a control pattern for a given <see cref="AutomationElement"/>. 
        /// </summary>
        /// <typeparam name="T">The return type of the <see cref="AutomationPattern"/> to return.</typeparam>
        /// <param name="element">The element to retrieve the pattern for.</param>
        /// <param name="automationPattern">The <see cref="AutomationPattern"/> to get.</param>
        /// <returns>The control pattern if it is supported; otherwise Nothing.</returns>
        public static T GetPattern<T>(AutomationElement element, AutomationPattern automationPattern) where T : BasePattern
        {
            object pattern;
            if (element.TryGetCurrentPattern(automationPattern, out pattern))
            {
                return pattern as T; 
            }
            return null; 
        }

        /// <summary>
        /// Expands an element, displaying all child nodes, controls, or content of the <see cref="AutomationElement"/>.
        /// </summary>
        /// <param name="element">The element to expand.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void Expand(AutomationElement element)
        {
            var pattern = (ExpandCollapsePattern) element.GetCurrentPattern(ExpandCollapsePattern.Pattern);
            pattern.Expand(); 
        }

        /// <summary>
        /// Collapses an element, hiding all child nodes, controls, or content of the <see cref="AutomationElement"/>.
        /// </summary>
        /// <param name="element">The element to collapse.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void Collapse(AutomationElement element)
        {
            var pattern = (ExpandCollapsePattern) element.GetCurrentPattern(ExpandCollapsePattern.Pattern);
            pattern.Collapse();
        }

        /// <summary>
        /// Sets the dock position of a dockable control.
        /// </summary>
        /// <param name="element">The control to dock.</param>
        /// <param name="position">The new position of the dock.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void Dock(AutomationElement element, DockPosition position)
        {
            var pattern = (DockPattern) element.GetCurrentPattern(DockPattern.Pattern);
            pattern.SetDockPosition(position);
        }

        /// <summary>
        /// Retrieves an AutomationElement that represents the specified cell in a table or grid control. 
        /// </summary>
        /// <param name="element">The table or grid control type element.</param>
        /// <param name="row">The ordinal number of the row of interest.</param>
        /// <param name="column">The ordinal number of the column of interest.</param>
        /// <returns>AutomationElement that represents the retrieved cell.</returns>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        /// <remarks>Grid coordinates are zero-based with the upper left (or upper right cell depending on locale) having coordinates (0,0).</remarks>
        public static AutomationElement GetItem(AutomationElement element, int row, int column)
        {
            var pattern = (GridPattern) element.GetCurrentPattern(GridPattern.Pattern);
            return pattern.GetItem(row, column);
        }

        /// <summary>
        /// Gets the container of a cell or item. 
        /// </summary>
        /// <param name="element">The element to get the container of.</param>
        /// <returns>An <see cref="AutomationElement"/> that represents the container of a cell or item.</returns>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static AutomationElement GetContainingGrid(AutomationElement element)
        {
            var pattern = (GridItemPattern) element.GetCurrentPattern(GridItemPattern.Pattern);
            return pattern.Current.ContainingGrid; 
        }

        /// <summary>
        /// Closes the specified window. 
        /// </summary>
        /// <param name="window">The window element to close.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void Close(AutomationElement window)
        {
            var pattern = (WindowPattern) window.GetCurrentPattern(WindowPattern.Pattern);
            pattern.Close();
        }

        /// <summary>
        /// Maximizes the specified window. 
        /// </summary>
        /// <param name="window">The window element to maximize.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void Maximize(AutomationElement window)
        {
            var pattern = (WindowPattern) window.GetCurrentPattern(WindowPattern.Pattern); 
            pattern.SetWindowVisualState(WindowVisualState.Maximized); 
        }

        /// <summary>
        /// Minimizes the specified window. 
        /// </summary>
        /// <param name="window">The window element to minimize.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void Minimize(AutomationElement window)
        {
            var pattern = (WindowPattern) window.GetCurrentPattern(WindowPattern.Pattern);
            pattern.SetWindowVisualState(WindowVisualState.Minimized);
        }

        /// <summary>
        /// Restores the specified window to its normal state. 
        /// </summary>
        /// <param name="window">The window element to restore.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void Restore(AutomationElement window)
        {
            var pattern = (WindowPattern) window.GetCurrentPattern(WindowPattern.Pattern);
            pattern.SetWindowVisualState(WindowVisualState.Normal); 
        }

        /// <summary>
        /// Causes the calling code to block for the specified time or until the associated process enters an idle state, whichever completes first.
        /// </summary>
        /// <param name="window">The window element to wait on.</param>
        /// <param name="milliseconds">The amount of time, in milliseconds, to wait for the associated process to become idle.</param>
        /// <returns>true if the window has entered the idle state; false if the timeout occurred.</returns>
        /// <exception cref="ArgumentOutOfRangeException">When the time parameter passed in is not a valid number.</exception>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static bool WaitForInputIdle(AutomationElement window, int milliseconds)
        {
            var pattern = (WindowPattern)window.GetCurrentPattern(WindowPattern.Pattern);
            return pattern.WaitForInputIdle(milliseconds); 
        }

        /// <summary>
        /// Gets the parent selection container of a control. 
        /// </summary>
        /// <param name="element">The child element.</param>
        /// <returns>The <see cref="AutomationElement"/> of the parent control that implements the <see cref="SelectionPattern"/>.</returns>
        /// <exception cref="InvalidOperationException">The control pattern cannot be performed by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static AutomationElement GetSelectionContainer(AutomationElement element)
        {
            var pattern = (SelectionItemPattern) element.GetCurrentPattern(SelectionItemPattern.Pattern);
            return pattern.Current.SelectionContainer;
        }

        /// <summary>
        /// Deselects any selected items and then selects the current element.
        /// </summary>
        /// <param name="element">The element to select</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        /// <remarks>Typically used for individual items in selection container controls, such as list boxes and combo boxes.</remarks>
        public static void Select(AutomationElement element)
        {
            var pattern = (SelectionItemPattern) element.GetCurrentPattern(SelectionItemPattern.Pattern);
            pattern.Select();
        }

        /// <summary>
        /// Adds the current element to the collection of the selected elements.
        /// </summary>
        /// <param name="element">The element to add to the current selection.</param>
        /// <exception cref="InvalidOperationException">The control pattern cannot be performed by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void AddToSelection(AutomationElement element)
        {
            var pattern = (SelectionItemPattern) element.GetCurrentPattern(SelectionItemPattern.Pattern);
            pattern.AddToSelection();
        }

        /// <summary>
        /// Removes the current element to the collection of selected elements. 
        /// </summary>
        /// <param name="element">The element to remove from the current selection.</param>
        /// <exception cref="InvalidOperationException">The control pattern cannot be performed by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void RemoveFromSelection(AutomationElement element)
        {
            var pattern = (SelectionItemPattern) element.GetCurrentPattern(SelectionItemPattern.Pattern);
            pattern.RemoveFromSelection();
        }

        /// <summary>
        /// Scrolls the visible region of the content area horizontally and vertically. 
        /// </summary>
        /// <param name="element">The element to be scrolled.</param>
        /// <param name="horizontalAmount">The horizontal scrolling increment that is specific to the control.</param>
        /// <param name="verticalAmount">The vertical scrolling increment that is specific to the control.</param>
        /// <exception cref="InvalidOperationException">The control pattern cannot be performed by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        /// <remarks>No scroll should be passed if if the control cannot be scrolled in a given direction.</remarks>
        public static void Scroll(AutomationElement element, ScrollAmount horizontalAmount, ScrollAmount verticalAmount)
        {
            var pattern = (ScrollPattern) element.GetCurrentPattern(ScrollPattern.Pattern); 
            pattern.Scroll(horizontalAmount, verticalAmount);
        }

        /// <summary>
        /// Sets the horizontal and/or vertical scroll of the element.  
        /// </summary>
        /// <param name="element">The element to scroll.</param>
        /// <param name="horizontalPercent">
        /// The percentage of the total horizontal content area. 
        /// NoScroll should be passed in if the control cannot be scrolled in this direction.
        /// </param>
        /// <param name="verticalPercent">
        /// The percentage of the total vertical content area. 
        /// NoScroll should be passed in if the control cannot be scrolled in this direction.
        /// </param>
        public static void SetScrollPercent(AutomationElement element, double horizontalPercent, double verticalPercent)
        {
            var pattern = (ScrollPattern)element.GetCurrentPattern(ScrollPattern.Pattern); 
            pattern.SetScrollPercent(horizontalPercent, verticalPercent);
        }

        /// <summary>
        /// Scroll an element horizontally by the <see cref="ScrollAmount"/>. 
        /// </summary>
        /// <param name="element">The element to scroll.</param>
        /// <param name="amount">The amount to scroll horizontally.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void ScrollHorizontally(AutomationElement element, ScrollAmount amount)
        {
            var pattern = (ScrollPattern) element.GetCurrentPattern(ScrollPattern.Pattern);
            pattern.ScrollHorizontal(amount);
        }

        /// <summary>
        /// Scroll an element vertically by the <see cref="ScrollAmount"/>. 
        /// </summary>
        /// <param name="element">The element to scroll.</param>
        /// <param name="amount">The amount to scroll vertically.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void ScrollVertical(AutomationElement element, ScrollAmount amount)
        {
            var pattern = (ScrollPattern) element.GetCurrentPattern(ScrollPattern.Pattern);
            pattern.ScrollVertical(amount); 
        }

        /// <summary>
        /// Scrolls the content area of a container object in order to display the AutomationElement within the visible region (viewport) of the container.
        /// </summary>
        /// <param name="element">The element to scroll into view.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void ScrollIntoView(AutomationElement element)
        {
            var pattern = (ScrollItemPattern) element.GetCurrentPattern(ScrollItemPattern.Pattern);
            pattern.ScrollIntoView();
        }

        /// <summary>
        /// Sets the scroll percentage to the top of the control.
        /// </summary>
        /// <param name="element">The element to scroll.</param>
        public static void ScrollToTop(AutomationElement element)
        {
            var pattern = (ScrollPattern)element.GetCurrentPattern(ScrollPattern.Pattern);
            pattern.SetScrollPercent(ScrollPattern.NoScroll, 0);
        }
        
        /// <summary>
        /// Calls invoke a control to initiate or perform a single, unambiguous action that does not maintain state when activated.
        /// </summary>
        /// <param name="element">The element to invoke.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        /// <remarks>Typically used for controls that can be invoked, such as a button.</remarks>
        public static void Invoke(AutomationElement element)
        {
            var pattern = (InvokePattern) element.GetCurrentPattern(InvokePattern.Pattern);
            pattern.Invoke(); 
        }

        /// <summary>
        /// Sets for the value for a range control such as a spinner control.
        /// </summary>
        /// <param name="element">The target element to set the range value.</param>
        /// <param name="value">The new value of the element.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        /// <exception cref="ArgumentOutOfRangeException"><see cref="value"/> is less than the minimum or greater than the maximum value of the element.</exception>
        public static void SetRangeValue(AutomationElement element, double value)
        {
            var pattern = (RangeValuePattern) element.GetCurrentPattern(RangeValuePattern.Pattern);
            pattern.SetValue(value);
        }

        /// <summary>
        /// Gets the current value for a range control.   
        /// </summary>
        /// <param name="element">The target element to get the range value.</param>
        /// <returns>The current value of the range control.</returns>
        /// <exception cref="ArgumentException">Thrown if element does not support the required <see cref="RangeValuePattern"/>.</exception>
        public static double GetRangeValue(AutomationElement element)
        {
            var pattern = (RangeValuePattern) element.GetCurrentPattern(RangeValuePattern.Pattern);
            return pattern.Current.Value; 
        }

        /// <summary>
        /// Inserts a string into a control that supports the value pattern. 
        /// </summary>
        /// <param name="element">The target control.</param>
        /// <param name="value">The string to be inserted.</param>
        public static void SetValue(AutomationElement element, string value)
        {
            var pattern = (ValuePattern) element.GetCurrentPattern(ValuePattern.Pattern); 
            pattern.SetValue(value);
        }

        /// <summary>
        /// Toggles the specified control. 
        /// </summary>
        /// <param name="element">The control to be toggled.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        /// <remarks>A control will cycle through its ToggleState in this order: On, Off and, if supported, Indeterminate.</remarks>
        public static void Toggle(AutomationElement element)
        {
            var pattern = (TogglePattern) element.GetCurrentPattern(TogglePattern.Pattern);
            pattern.Toggle();
        }

        /// <summary>
        /// Sets the toggle state of a control to On. 
        /// </summary>
        /// <param name="element">The control to toggle on.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void ToggleOn(AutomationElement element)
        {
            var pattern = (TogglePattern) element.GetCurrentPattern(TogglePattern.Pattern);
            if (pattern.Current.ToggleState == ToggleState.Off)
            {
                pattern.Toggle();
            }
            if (pattern.Current.ToggleState == ToggleState.Indeterminate)
            {
                pattern.Toggle();
            }
        }

        /// <summary>
        /// Sets the toggle state of a control to Off. 
        /// </summary>
        /// <param name="element">The control to toggle off.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void ToggleOff(AutomationElement element)
        {
            var pattern = (TogglePattern) element.GetCurrentPattern(TogglePattern.Pattern);

            if (pattern.Current.ToggleState == ToggleState.Indeterminate)
            {
                pattern.Toggle();
            }
            if (pattern.Current.ToggleState == ToggleState.On)
            {
                pattern.Toggle();
            }
        }

        /// <summary>
        /// Moves a control. 
        /// </summary>
        /// <param name="element">The control to move</param>
        /// <param name="x">Absolute screen coordinates of the left side of the control.</param>
        /// <param name="y">Absolute screen coordinates of the top of the control.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void Move(AutomationElement element, double x, double y)
        {
            var pattern = (TransformPattern) element.GetCurrentPattern(TransformPattern.Pattern);
            pattern.Move(x, y);
        }

        /// <summary>
        /// Resizes a control. 
        /// </summary>
        /// <param name="element">The control to resize</param>
        /// <param name="width">The new width of the window, in pixels.</param>
        /// <param name="height">The new height of the window, in pixels.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void Resize(AutomationElement element, double width, double height)
        {
            var pattern = (TransformPattern) element.GetCurrentPattern(TransformPattern.Pattern);
            pattern.Resize(width, height);
        }

        /// <summary>
        /// Rotates a control. 
        /// </summary>
        /// <param name="element">The control to rotate</param>
        /// <param name="degrees">The number of degrees to rotate the control. A positive number rotates clockwise; a negative number rotates counterclockwise.</param>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static void Rotate(AutomationElement element, double degrees)
        {
            var pattern = (TransformPattern) element.GetCurrentPattern(TransformPattern.Pattern);
            pattern.Rotate(degrees);
        }

        /// <summary>
        /// Gets a collection of automation elements that represents all the column headers in a table.
        /// </summary>
        /// <param name="element">The table control to retrieve the column headers from.</param>
        /// <returns>A collection of elements representing the column headers.</returns>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static AutomationElement[] GetColumnHeaders(AutomationElement element)
        {
            var pattern = (TablePattern) element.GetCurrentPattern(TablePattern.Pattern);
            return pattern.Current.GetColumnHeaders();
        }

        /// <summary>
        /// Gets a collection of column headers associated with a table item or cell. 
        /// </summary>
        /// <param name="element">The table control to retrieve the column headers from.</param>
        /// <returns>A collection of elements representing the column headers.</returns>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static AutomationElement[] GetColumnHeaderItems(AutomationElement element)
        {
            var pattern = (TableItemPattern)element.GetCurrentPattern(TableItemPattern.Pattern);
            return pattern.Current.GetColumnHeaderItems();
        }

        /// <summary>
        /// Gets a collection of row headers associated with a table item or cell. 
        /// </summary>
        /// <param name="element">The table control to retrieve the column headers from.</param>
        /// <returns>A collection of elements representing the column headers.</returns>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static AutomationElement[] GetRowHeaderItems(AutomationElement element)
        {
            var pattern = (TableItemPattern)element.GetCurrentPattern(TableItemPattern.Pattern);
            return pattern.Current.GetRowHeaderItems();
        }

        /// <summary>
        /// Gets a collection of automation elements that represents all the row headers in a table.
        /// </summary>
        /// <param name="element">The table control to retrieve the row headers from.</param>
        /// <returns>A collection of elements representing the row headers.</returns>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static AutomationElement[] GetRowHeaders(AutomationElement element)
        {
            var pattern = (TablePattern) element.GetCurrentPattern(TablePattern.Pattern);
            return pattern.Current.GetRowHeaders();
        }

        /// <summary>
        /// Gets a collection of automation elements that represents the current selection for an element.
        /// </summary>
        /// <param name="element">The element to get the selection from.</param>
        /// <returns>A collection of elements representing the current selection.</returns>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static AutomationElement[] GetSelection(AutomationElement element)
        {
            var pattern = (SelectionPattern) element.GetCurrentPattern(SelectionPattern.Pattern);
            return pattern.Current.GetSelection(); 
        }

        /// <summary>
        /// Retrieves a collection of integer values that identify the views available for a control.
        /// </summary>
        /// <param name="element">The element to get views from.</param>
        /// <returns>A collection of view identifiers. An empty collection is returned if there are no found view identifiers.</returns>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static int[] GetSupportedViews(AutomationElement element)
        {
            var pattern = (MultipleViewPattern) element.GetCurrentPattern(MultipleViewPattern.Pattern); 
            return pattern.Current.GetSupportedViews(); 
        }

        /// <summary>
        /// Gets the name of a control specific view. 
        /// </summary>
        /// <param name="element">The element to get the view name of.</param>
        /// <param name="viewId">A view identifier.</param>
        /// <returns>The localized name of the view.</returns>
        /// <exception cref="InvalidOperationException">The pattern is not supported by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The element is not available anymore.</exception>
        public static string GetViewName(AutomationElement element, int viewId)
        {
            var pattern = (MultipleViewPattern) element.GetCurrentPattern(MultipleViewPattern.Pattern);
            return pattern.GetViewName(viewId);
        }

        /// <summary>
        /// Retrieves a collection of text ranges that represents the currently selected text in a text-based control
        /// </summary>
        /// <param name="element">The element to get the text selection from.</param>
        /// <returns>A collection of disjoint text ranges.</returns>
        /// <exception cref="InvalidOperationException">The control pattern cannot be performed by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The current element does not exist anymore.</exception>
        public static TextPatternRange[] GetTextSelection(AutomationElement element)
        {
            var pattern = (TextPattern) element.GetCurrentPattern(TextPattern.Pattern);
            return pattern.GetSelection();
        }

        /// <summary>
        /// Retrieves an array of disjoint text ranges from a text-based control where each text range represents a contiguous span of visible text.
        /// </summary>
        /// <param name="element">The element to get the visible text ranges from.</param>
        /// <returns>The collection of visible text ranges within the container or an empty array.</returns>
        /// <exception cref="InvalidOperationException">The control pattern cannot be performed by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The current element does not exist anymore.</exception>
        public static TextPatternRange[] GetVisibleRanges(AutomationElement element)
        {
            var pattern = (TextPattern) element.GetCurrentPattern(TextPattern.Pattern);
            return pattern.GetVisibleRanges();
        }

        /// <summary>
        /// Retrieves a text range enclosing a child element such as an image, hyperlink, Microsoft Excel spreadsheet, or other embedded object. 
        /// </summary>
        /// <param name="element">The parent element.</param>
        /// <param name="childElement">The enclosed object.</param>
        /// <returns>A range that spans the child element.</returns>
        /// <exception cref="ArgumentNullException">The child element is null.</exception>
        /// <exception cref="InvalidOperationException">The control pattern cannot be performed by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The current element does not exist anymore.</exception>
        public static TextPatternRange RangeFromChild(AutomationElement element, AutomationElement childElement)
        {
            var pattern = (TextPattern) element.GetCurrentPattern(TextPattern.Pattern);
            return pattern.RangeFromChild(childElement); 
        }

        /// <summary>
        /// Returns the degenerate (empty) text range nearest to the specified screen coordinates. 
        /// </summary>
        /// <param name="element">The element to get the range from.</param>
        /// <param name="screenLocation">The location in screen coordinates.</param>
        /// <returns>A degenerate range nearest the specified location. Null is never returned.</returns>
        /// <exception cref="ArgumentException">A given point is outside the <see cref="AutomationElement"/> associated with the text pattern.</exception>
        /// <exception cref="InvalidOperationException">The control pattern cannot be performed by the element.</exception>
        /// <exception cref="ElementNotAvailableException">The current element does not exist anymore.</exception>
        public static TextPatternRange RangeFromPoint(AutomationElement element, Point screenLocation)
        {
            var pattern = (TextPattern) element.GetCurrentPattern(TextPattern.Pattern);
            return pattern.RangeFromPoint(screenLocation);
        }
    }
}
